package com.dream.plugin.simpledao.core;

import com.dream.plugin.simpledao.util.SConsoleTip;
import com.dream.plugin.simpledao.exception.ScanTableException;
import com.dream.plugin.simpledao.exception.TableIdNotExistsException;
import com.dream.plugin.simpledao.exception.TableIsEmptyException;
import com.dream.plugin.simpledao.tag.SColumn;
import com.dream.plugin.simpledao.tag.SId;
import com.dream.plugin.simpledao.tag.STable;
import com.dream.plugin.simpledao.util.SStringUtil;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.*;

/**
 * 扫描实体类，添加到sql容器
 */
public class SScanTable {

    private final String URL_PROTOCOL = "file";

    // sql容器
    private SContainer sContainer = SContainer.INSTANCE;


    public SScanTable() {
        init("");
    }

    public SScanTable(String basePackage) {
        init(basePackage);
    }

    /**
     * 1. 扫描所有被 @STable 注解的实体类
     * 2. 将实体类信息放入容器中
     *
     * @param basePackage 扫描的起始位置
     */
    private void init(String basePackage) {
        if (basePackage == null) {
            basePackage = "";
        }
        try {
            SConsoleTip.initOut("开始扫描数据库表格:" + ("".equals(basePackage) ? "从项目根目录开始扫描" : basePackage));
            scanClassAnnotationWithSTable(basePackage);
            SConsoleTip.initOut("数据库表格实体类扫描完成:" + String.join(",", sContainer.getTableName()));
        } catch (Exception e) {
            e.printStackTrace();
            throw new ScanTableException();
        }
    }

    /**
     * 递归扫描带 @STable 注解的class 并注入到容器中
     *
     * @param packagePath
     */
    private void scanClassAnnotationWithSTable(String packagePath) throws IOException, ClassNotFoundException {
        Enumeration<URL> iterator = Thread.currentThread().getContextClassLoader().getResources(packagePath.replace(".", "/"));
        while (iterator.hasMoreElements()) {
            URL url = iterator.nextElement();
            if (URL_PROTOCOL.equals(url.getProtocol())) {
                File packageFile = new File(url.getPath());
                if (packageFile.isDirectory()) {
                    // 获取当前包下的文件
                    for (File file : Objects.requireNonNull(packageFile.listFiles())) {
                        if (file.isFile() && file.getName().endsWith(".class")) {
                            String classPath = (packagePath + "." + file.getName());
                            classPath = classPath.substring(0, classPath.lastIndexOf('.'));
                            Class c = Thread.currentThread().getContextClassLoader().loadClass(classPath);
                            savePojoToContainer(c);
                        } else if (file.isDirectory()) {
                            String nextPackagePath = "".equals(packagePath) ? file.getName() : packagePath + "." + file.getName();
                            scanClassAnnotationWithSTable(nextPackagePath);
                        }
                    }
                }
            }
        }
    }

    /**
     * 将class校验成功后注入容器
     * @param pojo
     */
    private void savePojoToContainer(Class<?> pojo) {
        if (!pojo.isAnnotationPresent(STable.class)) {
            // 如果没有@STable注解则不注入
            return;
        }
        String tableName = SStringUtil.getNameOfTable(pojo);
        if (SStringUtil.isEmpty(tableName)){
            throw new ScanTableException();
        }
        // 放入tablename
        List<String> columns = new ArrayList<>();
        String idName = null;
        Field[] fields = pojo.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(SId.class)) {
                // 放入id
                idName = SStringUtil.getNameOfId(field);
                columns.add(idName);
                continue;
            }
            if (field.isAnnotationPresent(SColumn.class)) {
                // 放入字段
                String columnName = SStringUtil.getNameOfColumn(field);
                columns.add(columnName);
            }
        }
        // 数据库表的字段至少2个，否则判断为空表
        if (columns.size() <= 1)
            throw new TableIsEmptyException(tableName);
        if (SStringUtil.isEmpty(idName)){
            throw new TableIdNotExistsException(tableName);
        }
        // 将数据库表信息放入容器
        sContainer.saveTableInfo(pojo, tableName, idName, columns);
    }

}
